//
//  String+Ext.swift
//  Tuhu
//
//  Created by 丁帅 on 2017/5/3.
//  Copyright © 2017年 Tuhu. All rights reserved.
//

import Foundation
import UIKit

// MARK: - Common
public extension String {
    /// 返回字符串所表示的十六进制数值，支持"#"标识符， 支持 "-" 号
    var hexInteger: Int {
        var temp = self.uppercased()
        if temp.hasPrefix("#") {
            temp = temp.replacingOccurrences(of: "#", with: "")
        }
        var value = 0
        var negativeSign = 1
        var breakLoop = false
        for (idx, char) in temp.enumerated() {
            guard !breakLoop else { break }
            value *= 16
            switch char {
            case "-" where idx == 0:
                negativeSign = -1
            case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
                value += Int(String(char))!
            case "A":
                value += 10
            case "B":
                value += 11
            case "C":
                value += 12
            case "D":
                value += 13
            case "E":
                value += 14
            case "F":
                value += 15
            default:
                breakLoop = true
                break
            }
        }
        return value * negativeSign
    }
}

// MARK: - Range Replace
public extension String {

    /// 通过 _(location: Int, length: Int)_ 获取子字符串，获取失败时，返回空；
    ///
    ///  _(Int, Int)_ 元组支持 **Negative Number** 参数：**Nonnegative Number** 表示起始位置和长度;  **Negative Number** 表示从字符串末尾向前的偏移量
    ///
    /// - eg:
    ///
    ///     let str = "abcdefg"
    ///
    ///     str.substring(in: (1, 2))       // "bc"
    ///
    ///     str.substring(in: (1, -1))      // "bcdef"
    ///
    ///     str.substring(in: (-3, 2))      // "ef"
    ///
    ///     str.substring(in: (-4, -1))     // "def"
    ///
    ///     str.substring(in: (5, 10))      // "" 超出范围
    ///
    ///     str.substring(in: (-1, -3))     // "" 终止位置小于起始位置
    ///
    /// - Parameter subrange: 子字符串范围：
    ///
    ///          location 开始位置:  非负数 表示从起始位置向后的偏移量; 负数 表示从末尾位置向前的偏移量
    ///          length 长度:  非负数 表示从开始位置向后的长度; 负数 表示从末尾位置向前的偏移量
    ///
    /// - Returns: 返回 获取的子字符串，如果 _subrange_ 不可用，返回 _nil_.
    func substring(in subrange: (location: Int, length: Int)) -> String? {
        guard let range = range(subrange.location, subrange.length) else { return nil }
        return String(self[range])
    }

    /// 通过 _NSRange_ 获取子字符串，获取失败时，返回空；规则参考 **substring(in: (Int, Int))**
    ///
    /// - Parameter subrange: 子字符串范围.
    /// - Returns: 返回 获取的子字符串，如果 _subrange_ 不可用，返回 _nil_.
    func substring(in subrange: NSRange) -> String? {
        return substring(in: (subrange.location, subrange.length))
    }

    /// 通过 _(location: Int, length: Int)_ 替换子字符串，并返回 替换 后的新字符串；规则参考 **substring(in: (Int, Int))**
    ///
    ///
    /// - Parameters:
    ///   - subrange: 子字符串范围.
    ///   - replacement: 要替换的字符串
    ///
    /// - Returns: 返回 替换后的新字符串，如果 _subrange_ 不可用， 返回值与自身相同.
    func replacing(in subrange: (location: Int, length: Int), with replacement: String) -> String {
        guard let range = range(subrange.location, subrange.length) else { return self }
        return replacingCharacters(in: range, with: replacement)
    }

    /// 通过 _(location: Int, length: Int)_ 替换子字符串, 如果 _subrange_ 不可用, 不做任何事；规则参考 **substring(in: (Int, Int))**
    ///
    /// - Parameters:
    ///   - subrange: 子字符串范围：
    ///   - replacement: 要替换的字符串
    mutating func replace(in subrange: (location: Int, length: Int), with replacement: String) {
        guard let range = range(subrange.location, subrange.length) else { return }
        replaceSubrange(range, with: replacement)
    }

    /// 通过 _NSRange_ 替换子字符串，并返回 替换 后的新字符串；规则参考 **substring(in: (Int, Int))**
    ///
    /// - Parameters:
    ///   - subrange: 子字符串范围
    ///   - replacement: 要替换的字符串
    /// - Returns: 返回 替换后的新字符串，如果 _subrange_ 不可用， 返回值与自身相同.
    func replacing(in subrange: NSRange, with replacement: String) -> String {
        guard let range = range(subrange.location, subrange.length) else { return self }
        return replacingCharacters(in: range, with: replacement)
    }

    /// 通过 _NSRange_ 替换子字符串, 如果 _Range_ 不可用, 不做任何事；规则参考 **substring(in: (Int, Int))**
    ///
    /// - Parameters:
    ///   - subrange: 子字符串范围
    ///   - replacement: 要替换的字符串
    mutating func replace(in subrange: NSRange, with replacement: String) {
        guard let range = range(subrange.location, subrange.length) else { return }
        replaceSubrange(range, with: replacement)
    }

    /// 通过 _(location: Int, length: Int)_下标 获取/替换 子字符串，获取失败时，返回空字符串； 替换失败时，不做操作
    ///
    ///  支持 **Negative Number** 参数； 规则参考  **substring(in: (Int, Int))**
    ///
    /// - Parameters:
    ///   - location: 开始位置
    ///   - length: 长度
    subscript(location: Int, length: Int) -> String {
        get {
            return substring(in: (location, length)) ?? ""
        }
        set {
            guard let range = range(location, length) else { return }
            replaceSubrange(range, with: newValue)
        }
    }
    
    // 私有方法
    private func range(_ start: Int, _ length: Int) -> Range<String.Index>? {
        guard
            let lower = index(startIndex)(start),
            let upper = index(lower)(length)
            else { return nil }
        return lower ..< upper
    }

    // 私有方法
    private func index(_ lmt: String.Index) -> (Int) -> String.Index? {
        return {
            return self.index($0 >= 0 ? lmt : self.endIndex, offsetBy: $0, limitedBy: $0 >= 0 ? self.endIndex : lmt)
        }
    }
}

// MARK: - RegEx
public extension String {
    /// 判断字符串是否满足正则匹配
    ///
    /// - Parameter regex: 要匹配的正则表达式
    /// - Returns: 是否满足正则匹配
    func match(_ regex: String) -> Bool {
        guard let result = regexResult(regex) else { return false }
        return result.numberOfRanges > 0
    }

    /// 获取正则匹配成功的第一个子字符串
    ///
    /// - Parameters:
    ///   - pattern: 要匹配的正则表达式
    ///   - index: 匹配结果的位置：0 表示整个匹配字符串，大于0 表示子条件匹配的字符串，顺序自左向右
    /// - Returns: 返回匹配到的字符串，匹配失败则返回空
    func substring(byMatch pattern: String, capture index: Int = 0) -> String? {
        guard index >= 0 else { return nil }
        guard let result = regexResult(pattern) else { return nil }
        guard index < result.numberOfRanges else { return nil }
        let range = result.range(at: index)
        return self[range.location, range.length]
    }

    /// 正则匹配替换, 返回把所有正则匹配到的字符串替换成目标字符串的新字符串
    ///
    /// - Parameters:
    ///   - pattern: 要匹配的正则表达式
    ///   - replaceString: 替换的目标字符串，默认是 ""
    /// - Returns: 替换后的新字符串
    func replacing(byMatch pattern: String, with replaceString: String = "") -> String {
        guard let regex: NSRegularExpression = try? NSRegularExpression(pattern: pattern, options: []) else { return self }
        return regex.stringByReplacingMatches(in: self, options: [], range: NSMakeRange(0, count), withTemplate: replaceString)
    }

    /// 正则匹配替换, 替换自身匹配到的所有字符串
    ///
    /// - Parameters:
    ///   - pattern: 要匹配的正则表达式
    ///   - replaceString: 替换的目标字符串，默认是 ""
    mutating func replac(byMatch pattern: String, with replaceString: String = "") {
        self = replacing(byMatch: pattern, with: replaceString)
    }

    // 私有方法，返回第一次正则匹配结果
    private func regexResult(_ pattern: String) -> NSTextCheckingResult? {
        guard let regex: NSRegularExpression = try? NSRegularExpression(pattern: pattern, options: []) else { return nil }
        guard let result = regex.firstMatch(in: self, options: [], range: NSRange(location: 0, length: count)) else { return nil }
        return result
    }
}

// MARK: - 计算显示大小
public extension String {

    /// 计算字符串绘制的尺寸
    ///
    /// - Parameters:
    ///   - fontSize: 字体大小，系统默认字体，如果要使用其他字体，在 _othersAttribute_ 中覆盖
    ///   - size: 绘制的限制尺寸，最终的结果会在限制尺寸内
    ///   - otherAttr: text attributes, 包含字体，颜色，段落格式等，默认为 nil
    /// - Returns: 字符串的绘制尺寸
//    func layoutSize(with fontSize: CGFloat, limit size: CGSize, otherAttributes othersAttrs: [NSAttributedString.Key: Any]? = nil) -> CGSize {
//        var attr: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: fontSize)]
//        if let othersAttrs = othersAttrs {
//            attr += othersAttrs
//        }
//        return (self as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attr, context: nil).size
//    }
}

public extension String {
    /// md5加密
//    var md5: String { return CommonMD5(self) }

    // base64编码
    var base64Encoding: String { return data(using: .utf8)?.base64EncodedString() ?? "" }
    var base64Decoding: String? {
        guard let data = Data(base64Encoded: self) else { return nil }
        return String(data: data, encoding: .utf8)
    }
}

public extension String {
    // 判断字符串是否有`watermark`水印
    func hasWatermark() -> Bool {
        return self.contains("watermark")
    }
}

public extension String {
    /// url query encode
    func encodeURLQuery() -> String? {
        return self.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
    }
    /// urldecode
    func decodeURL() -> String? {
        return self.removingPercentEncoding
    }
}

public extension String {
    static let RMB = "¥"
}

public extension String {
    /// 截取字符串
    ///
    /// - Parameter index: 截取从index位开始之前的字符串
    /// - Returns: 返回一个新的字符串
    func mySubString(to index: Int) -> String {
        if self.count >= index{
            return String(self[..<self.index(self.startIndex, offsetBy: index)])
        }
        return self
    }
    /// 截取字符串
    ///
    /// - Parameter index: 截取从index位开始到末尾的字符串
    /// - Returns: 返回一个新的字符串
    func mySubString(from index: Int) -> String {
        if self.count > index{
            return String(self[self.index(self.startIndex, offsetBy: index)...])
        }
        return ""
        
    }
}

public extension String {
    
    ///获取简单NSAttributedString
    func attributedString(font: CGFloat, foregroundColor: UIColor) -> NSAttributedString{

        return attributedString(font: UIFont.systemFont(ofSize: font), foregroundColor: foregroundColor)
    }
    
    func attributedString(font: CGFloat, foregroundColor: UIColor, backgroundColor: UIColor) -> NSAttributedString{
        return attributedString(font: UIFont.systemFont(ofSize: font), foregroundColor: foregroundColor, backgroundColor: backgroundColor)
    }
    func attributedString(font: UIFont, foregroundColor: UIColor) -> NSAttributedString{
        
        return NSAttributedString.init(string: self, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: foregroundColor])
    }
    
    func attributedString(font: UIFont, foregroundColor: UIColor, backgroundColor: UIColor) -> NSAttributedString{
        return NSAttributedString.init(string: self, attributes: [NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: foregroundColor,NSAttributedString.Key.backgroundColor: backgroundColor])

    }
    
    func attributedStringLineSpacing(_ height: CGFloat) -> NSMutableAttributedString {
        return attributedStringLineSpacing(height, alignment: .left)
    }
    
    func attributedStringLineSpacing(_ height: CGFloat, alignment:NSTextAlignment = .left) -> NSMutableAttributedString {
        let attributedString = NSMutableAttributedString(string: self)
        
        // *** Create instance of `NSMutableParagraphStyle`
        let paragraphStyle = NSMutableParagraphStyle()
        
        // *** set LineSpacing property in points ***
        paragraphStyle.lineSpacing = height // Whatever line spacing you want in points
        paragraphStyle.alignment = alignment
        paragraphStyle.lineBreakMode = .byTruncatingTail
        
        // *** Apply attribute to string ***
        attributedString.addAttribute(.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
        
        // *** Set Attributed String to your label ***
        return attributedString
    }
}

// MARK: AttributedString

public extension NSAttributedString{
    func attributedString(lineSpacing: CGFloat = 4,
                                 alignment:NSTextAlignment = .left,
                                 lineBreakMode: NSLineBreakMode = .byTruncatingTail,
                                 headIndent: CGFloat = 0)
        -> NSMutableAttributedString
    {
        let atributedString = NSMutableAttributedString.init(attributedString: self)
        
        let paragraphStyle = NSMutableParagraphStyle()
        
        // *** set LineSpacing property in points ***
        paragraphStyle.lineSpacing = lineSpacing // Whatever line spacing you want in points
        
        paragraphStyle.lineBreakMode = lineBreakMode
        
        paragraphStyle.headIndent = headIndent
        paragraphStyle.firstLineHeadIndent = headIndent
        
        paragraphStyle.alignment = alignment
        
        // *** Apply attribute to string ***
        atributedString.addAttribute(.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, length))
        
        return atributedString
    }
}

public extension NSMutableAttributedString{
    func attributedStringLineSpacing(_ height: CGFloat){
        
        let paragraphStyle = NSMutableParagraphStyle()
        
        // *** set LineSpacing property in points ***
        paragraphStyle.lineSpacing = height // Whatever line spacing you want in points
        
        paragraphStyle.lineBreakMode = .byTruncatingTail
        
        // *** Apply attribute to string ***
        addAttribute(.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, length))

    }
    
    func attributedStringSet(lineSpacing: CGFloat = 4,
                                    alignment:NSTextAlignment = .left,
                                    lineBreakMode: NSLineBreakMode = .byTruncatingTail,
                                    headIndent: CGFloat = 0)
    {
        
        let paragraphStyle = NSMutableParagraphStyle()
        
        // *** set LineSpacing property in points ***
        paragraphStyle.lineSpacing = lineSpacing // Whatever line spacing you want in points
        
        paragraphStyle.headIndent = headIndent
        paragraphStyle.firstLineHeadIndent = headIndent
        
        paragraphStyle.lineBreakMode = lineBreakMode
        
        paragraphStyle.alignment = alignment
        
        // *** Apply attribute to string ***
        addAttribute(.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, length))
        
    }
}

public extension String {
    
    /// 根据正则表达式判断字符串格式
    func isValidFormat(regex: String) -> Bool {
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", regex)
        return predicate.evaluate(with:self)
    }
    
    /// 字符串转换时间戳 (since 1970)
    func timeInterval(_ format: String) -> TimeInterval? {
        let _format = DateFormatter()
        _format.dateFormat = format
        if let date = _format.date(from: self) {
            return date.timeIntervalSince1970
        }
        return nil
    }
    
    /// to URL
    func toURL() -> URL? {
        return URL(string: self)
    }
    
    /// NSRange
    func rangeOfAll() -> NSRange {
        return NSMakeRange(0, self.count)
    }
    
    
    /// 字符串截取 to Index
    func subString(to index: Int) -> String? {
        guard index < self.count else { return self }
        return String(self[..<self.index(self.startIndex, offsetBy: index)])
    }
    
    /// 字符串截取 from Index
    func subString(from index: Int) -> String? {
        guard index < self.count else { return nil }
        return String(self[self.index(self.startIndex, offsetBy: index)...])
    }
    
    /// 字符串截取 with NSRange
    func subString(with aRange: NSRange) -> String? {
        guard aRange.location+aRange.length <= self.count else {
            return nil
        }
        let start = self.index(self.startIndex, offsetBy: aRange.location)
        let end = self.index(self.startIndex, offsetBy: aRange.location+aRange.length)
        return String(self[start..<end])
    }
    
    /// triming in Stirng
    func trimmingCharacters(in string: String) -> String {
        return self.trimmingCharacters(in: CharacterSet.init(charactersIn: string))
    }
    
}

public extension String{
    /**
     Get the height with font.
     
     - parameter font:       The font.
     - parameter fixedWidth: The fixed width.
     
     - returns: The height.
     */
    func heightWithFont(font : UIFont = UIFont.systemFont(ofSize: 16), fixedWidth : CGFloat) -> CGFloat {
        
        guard self.count > 0 && fixedWidth > 0 else {
            
            return 0
        }
        
        let size = CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)
        let text = self as NSString
        let rect = text.boundingRect(with: size, options:.usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font : font], context:nil)
        return rect.size.height
    }

    /**
     Get the width with the string.
     
     - parameter font: The font.
     
     - returns: The string's width.
     */
    func widthWithFont(font : UIFont = UIFont.systemFont(ofSize: 16)) -> CGFloat {
        
        guard self.count > 0 else {
            
            return 0
        }
        
        let size = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 0)
        let text = self as NSString
        let rect = text.boundingRect(with: size, options:.usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font : font], context:nil)
        
        return rect.size.width
    }

}
